/*
 * https://gitee.com/baojuhua/enumify.js
 *
 * @desc Enumify 是将普通扁平Js对象转类似后端 Enum枚举对象类。枚举类型 是由基础整型数值类型的一组命名常量定义的值类型
 * @param {Object}         obj 对象
 * @param {Object|Boolean} opt 自定义配置
 *                             reverse:     是否进行键值互换，默认false ;
 *                             valueType:   值类型， 默认Number ;
 *                             hintConvert: 自定义隐式类型转换。
 * @return {Enumify}
 */
export default class Enumify {
  static ENUM_STRING_VALUE_OPTS = {
    valueType: v => v,
    hintConvert: (hint, k, v) => (hint == 'default' ? k : v),
  }
  constructor(obj, opt) {
    const initConf = {
      reverse: opt == true || (opt && opt.reverse),
      valueType: opt && typeof opt.valueType == 'function' ? opt.valueType : Number,
      hintConvert: opt && typeof opt.hintConvert == 'function' ? opt.hintConvert : (hint, k, v) => (['string'].includes(hint) ? k : v),
    }
    this.original = initConf.reverse ? Enumify.reverse(obj) : obj
    if (Array.isArray(this.original)) {
      for (let index = 0; index < this.original.length; index++) {
        const k = this.original[index]
        const v = initConf.valueType(index)
        this[k] = Object.freeze({ _ENUM_ITEM_: true, k, v, key: k, value: v, [Symbol.toPrimitive]: hint => initConf.hintConvert(hint, k, v) })
      }
    } else {
      for (let k in this.original) {
        const v = initConf.valueType(this.original[k])
        this[k] = Object.freeze({ _ENUM_ITEM_: true, k, v, key: k, value: v, [Symbol.toPrimitive]: hint => initConf.hintConvert(hint, k, v) })
      }
    } 
    this.initConf = initConf
  }
  /**
   * @desc  反转对象
   * @param {Object} obj 参考对象
   * @return {Object}
   */
  static reverse(obj) {
    if (Array.isArray(obj)) return obj.reverse()
    return Object.fromEntries(Object.entries(obj).map(([a, b]) => [b, a]))
  }
  /**
   * @desc  通过对象初始化对象
   * @param {Object} obj 参考对象
   * @param {Boolean} reverse 是否反转对象
   * @return {Enumify}
   */
  static init(obj, reverse) {
    return new Enumify(obj, reverse)
  }
  /**
   * @desc  通过字典初始化枚举
   * @param {Object} dict 字典对象 ({ 实际值:标签字符 })
   * @param {Object} dict 参考字典对象
   * @return {Enumify}
   */
  static initByDict(dict) {
    return new Enumify(dict, true)
  }
  /**
   * @desc  获取所有 key
   * @return {Array}
   */
  keys() {
    return Object.keys(this.original)
  }
  /**
   * @desc  获取所有value
   * @return {Array}
   */
  values() {
    return Object.values(this.original)
  }
  /**
   * @desc  判断是否包含其中的值
   * @param {Any} arr 判断的值或数组
   * @param {Boolean} isKeys arr是否为key集合
   * @return {Boolean}
   */
  includes(arr, isKeys) {
    const fn = item => (typeof item == 'object' && item._ENUM_ITEM_ ? (isKeys ? item.key : item.value) : item)
    const list = isKeys ? this.keys() : this.values()
    if (Array.isArray(arr)) return !!list.find(item => arr.includes(fn(item)))
    else return list.includes(fn(arr))
  }
  /**
   * @desc  排除值 返回新枚举对象
   * @param {Any} arr 判断的值或数组
   * @param {Boolean} isKeys arr是否为key集合
   * @return {Enumify}
   */
  exclude(arr, isKeys) {
    const dict = isKeys ? this.original : Enumify.reverse(this.original)
    const keyArr = (Array.isArray(arr) ? arr : [arr]).map(item => '' + (item && item._ENUM_ITEM_ ? item[isKeys ? 'k' : 'v'] : item))
    const newObj = Object.fromEntries(
      Object.keys(dict)
        .filter(k => !keyArr.includes(k))
        .map(k => (isKeys ? [k, dict[k]] : [dict[k], k]))
    )
    return new Enumify(newObj, this.initConf)
  }
  /**
   * @desc  过滤值 返回新枚举对象
   * @param {Any} arr 判断的值或数组
   * @param {Boolean} isKeys arr是否为key集合
   * @return {Enumify}
   */
  filter(arr, isKeys) {
    const dict = isKeys ? this.original : Enumify.reverse(this.original)
    const newObj = Object.fromEntries(
      (Array.isArray(arr) ? arr : [arr]).map(k => {
        k = k._ENUM_ITEM_ ? (isKeys ? k.k : k.v) : k
        return isKeys ? [k, dict[k]] : [dict[k], k]
      })
    )
    return new Enumify(newObj, this.initConf)
  }
  /**
   * @desc  转字典 例如 {0:'否',1:'是'}
   * @param {Boolean} freeze 是否冻结对象
   * @return {Object}
   */
  toDict(freeze) {
    const dict = Object.fromEntries(Object.entries(this.original).map(([a, b]) => [b, a]))
    return freeze == true ? Object.freeze(dict) : dict
  }
  /**
   * @desc  转选项  例如 [{label:'否',value:0},{label:'是',value:1}]
   * @param {String} t 自定义 label 字段
   * @param {String} v 自定义 value 字段
   * @return {Array}
   */
  toOptions(t, v) {
    return Object.entries(this.original).map(([k, i]) => ({ [t || 'label']: k, [v || 'value']: i }))
  }
  /**
   * @desc  克隆当前对象
   * @param {Object} newObj 自定义 覆盖枚举对象
   * @return {Enumify}
   */
  clone(newObj) {
    return new Enumify({ ...this.original, ...newObj }, this.initConf)
  }
}
